Transcrição
Precisamos exibir photo-list.component.html para que a listagem de fotos esteja funcionando bem no navegador. Para complicar um pouquinho, criaremos um novo componente, simplesmente para que haja dois componentes em photos.module.ts. Este componente exibirá um título, e permitirá que saibamos que ora exibiremos a listagem e ora carregaremos a página do formulário para cadastro de novas imagens.
No terminal, pausaremos o command-line interface com "Ctrl + C", e criaremos photo-form dentro da pasta "photos" usando o comando ng generate component photos/photo-form, e com isso ele aparece automaticamente em declarations de photos.module.ts. Salvaremos o arquivo.
Nosso photo-form.component.html terá a frase "photo-form works!" que manteremos para posteriormente verificarmos se tudo funciona como queremos; poderia ser um conteúdo qualquer. Em seguida, utilizaremos o comando ng serve --open e, no Visual Studio, teremos dois componentes com escopo de página, com uma granularidade alta — "photo-list" e "photo-form".
Se verificarmos a nossa aplicação, da maneira em que ela está estruturada, o primeiro componente a ser carregado e que pode ser conferido em index.html é <app-root>. Então não faz sentido incluirmos um selector de "photo-form" ou de "photo-list", já que isso será fixo. Queremos que o componente adequado seja carregado de acordo com o endereço que o usuário digitar.
É neste sentido que entraremos agora na parte de roteamento de uma Single Page Application. Sendo deste tipo, a página index.html se encarrega de exibir tudo o que a aplicação possui, sem carregar outras páginas. Como funciona esta lógica?
Sabemos que, se acessarmos http://localhost:4200/, a index.html será carregada, que por sua vez carregará, por padrão, <app-root>. Se em vez deste endereço usarmos http://localhost:4200/user/flavio, a impressão é de que queremos acessar uma rota do back end. Da mesma forma, http://localhost:4200/p/add parece uma requisição para o back end.
No entanto, não é assim que funciona. Para o Angular identificar o que será a index.html, ela não carregará <app-root>, e sim funcionará como uma grande lacuna, a qual, de acordo com os endereços digitados, fará um "de/para", verificando origem da rota e tomando uma ação adequada com o componente correspondente.
Chamamos esta rota de path, um caminho. Cada caminho da aplicação, do nosso sistema de rotas, estará associado a um componente. É como se o Angular, ao receber certo endereço no navegador, antes deste acessar o back end e tentar fazer uma requisição, intervirá e verificará se a rota é cadastrada por ele.
Se for este o caso, a requisição não irá ao back end, e a rota ficará com o Angular, que carregará o respectivo componente, interceptando a mudança do endereço, e verificando se é uma rota válida, e por fim carregando o respectivo componente. Caso não seja uma rota válida, a requisição segue para o back end.
O componente <app-root> continuará sendo o primeiro a ser carregado pela aplicação, e dentro de "app", criaremos o arquivo app.routing.module.ts, um módulo, sendo necessário importá-lo. Criaremos este arquivo manualmente, digitando seu conteúdo para entendermos melhor seu fundamento. Por ser um módulo, ele terá @NgModule, e uma constante denominada routes ("rotas"), que será um array JavaScript.
Este array terá a configuração de cada rota da nossa aplicação, e será responsável pelo carregando do componente adequado. Cada configuração é feita por meio de um objeto JavaScript no qual indicamos um path, isto é, para user/flavio (por enquanto fixo) carregaremos o component PhotoListComponent e faremos o auto import. A outra rota é para p/add, para o qual será carregado o componente PhotoFormComponent.
Porém, da maneira como está, teremos problemas, pois o Angular espera encontrar um objeto com propriedade Path. A tipagem estática do TypeScript nos ajuda nisso, então indicaremos que routes é do tipo Routes. Porém, ao tentarmos importá-lo, o Angular CLI não nos dará uma sugestão, pois seu módulo de rotas ainda não foi carregado.
Como queremos que ele não seja carregado ainda, o importaremos manualmente:
import { NgModule } from '@angular/core';
import { Routes } from '@angular/router';
import { PhotoListComponent } from './photos/photo-list/photo-list.component';
import { PhotoFormComponent } from './photos/photo-list/photo-form.component';
const routes: Routes = [
{ path: 'user/flavio', component: PhotoListComponent },
{ path: 'p/add', component: PhotoFormComponent }
];
@NgModule({
})
export class AppRoutingModule { }Ao fazermos isto, o programa entende que cada item do array precisa ser um objeto JavaScript com tais propriedades, e tudo está muito bem tipado. Continuando, em NgModule precisamos importar o RouterModule, que é o módulo de rotas do Angular. Entretanto, deste modo, o AppRoutingModule importa o RouterModule, o qual não está linkado ao arquivo de rotas que acabamos de criar (routes), ou seja, não há uma relação entre eles.
Então, não importaremos RouterModule diretamente em imports, e utilizaremos forRoot(). Sendo nossa aplicação localhost:4200, tudo o que for 4200/ terá relação com a rota raiz. Então, o resultado de forRoot() será importado, e levará em consideração esta lista de rotas (RouterModule).
@NgModule({
imports: [ RouterModule.forRoot(routes) ]
})É como se tivéssemos um RouterModule pré-configurado que conhece as rotas da aplicação de antemão, em vez de passarmos um RouterModule que não sabe de nada. Salvaremos o arquivo e acessaremos app.module.ts, e importaremos AppRoutingModule. Com isso, o BrowserModule virá junto, com as rotas pré-configuradas.
Dica: às vezes o Angular CLI fica um pouco perdido quando há importação de um arquivo de módulo de rotas. Então, antes de tudo, é recomendado pausar o terminal do Angular e em seguida rodá-lo novamente.
Abriremos o navegador e consultaremos o console. Não há nenhuma mensagem de erro, então vamos tentar acessar a rota digitando localhost:4200/user/flavio na barra de navegação. Nada acontece, nenhuma requisição ao back end é realizada, e nenhum dado é exibido. Por que?
O primeiro componente a ser carregado pela aplicação é app.component.html, certo? Sendo assim, precisaremos indicar nele que, de acordo com um determinado endereço, o componente correspondente deverá ser carregado, e que isso deve ser exibido neste mesmo arquivo. Para isso, usaremos a diretiva <router-outlet>, deixando o arquivo da seguinte forma:
<router-outlet></router-outlet>É dentro desta tag que deverá se exibir o componente. Salvaremos, retornaremos ao navegador, e nos depararemos com uma mensagem de erro indicando que <router-outlet> é um elemento desconhecido, e que não é um componente, solicitando a verificação de seu pertencimento a algum módulo.
O que acontece é que AppModule importa AppRoutingModule, bem como os arquivos de configuração de rotas. No entanto, não consegue importar as diretivas de rotas do módulo RouterModule. Para resolvermos isso, poderíamos simplesmente importá-lo em app.module.ts, porém optaremos por uma alternativa: em @NgModule de app.routing.module.ts, exportaremos RouterModule.
@NgModule({
imports: [
RouterModule.forRoot(routes)
],
exports: [ RouterModule ]
})Assim, informamos que quem for importar AppRoutingModule também receberá RouterModule, o que fará com que não haja responsabilidade de importá-lo. Com esta alteração, nada será modificado em app.module.ts, voltaremos ao navegador e acessaremos localhost:4200/user/flavio mais uma vez, e as imagens serão carregadas com sucesso. E se acessarmos localhost:4200/p/add, o outro componente também terá o carregamento bem sucedido.